/**
 * Copyright (c) 2017-present PlatformIO <contact@platformio.org>
 * All rights reserved.
 *
 * This source code is licensed under the license found in the LICENSE file in
 * the root directory of this source tree.
 */

import * as siNodeHelpers from 'si-node-helpers';

import os from 'os';
import vscode from 'vscode';

const https = require('https');
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');

export function disposeSubscriptions(subscriptions) {
  while (subscriptions.length) {
    subscriptions.pop().dispose();
  }
}

export async function notifyError(title, err) {
  const description = err.stack || err.toString();
  const ghbody = `# Description of problem
  Leave a comment...

  BEFORE SUBMITTING, PLEASE SEARCH FOR DUPLICATES IN
  - https://github.com/platformio/platformio-vscode-ide/issues?q=is%3Aissue+

  # Configuration

  VSCode: ${vscode.version}
  PIO IDE: v${getIDEVersion()}
  System: ${os.type()}, ${os.release()}, ${os.arch()}

  # Exception
  \`\`\`
  ${description}
  \`\`\`
  `;
  const reportUrl = siNodeHelpers.misc.getErrorReportUrl(title, ghbody);

  let action = 'Report a problem';
  if (!reportUrl.includes('issues/new')) {
    action = 'Check available solutions';
  }

  const selected = await vscode.window.showErrorMessage(
    description.substring(0, 700) + '...',
    action
  );
  if (selected === action) {
    vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(reportUrl));
  }
  console.error(err);
}

export function getIDEManifest() {
  return vscode.extensions.getExtension('mengning.si').packageJSON;
}

export function getIDEVersion() {
  return getIDEManifest().version;
}

export async function listCoreSerialPorts() {
  const script = `
import json
from superide.public import list_serial_ports

print(json.dumps(list_serial_ports()))
    `;
  const output = await siNodeHelpers.core.getCorePythonCommandOutput(['-c', script]);
  return JSON.parse(output.trim()).map((item) => {
    for (const key of ['description', 'hwid']) {
      if (item[key] === 'n/a') {
        item[key] = undefined;
      }
    }
    return item;
  });
}

export function isInWSL() {
  const { execSync } = require('child_process');

  try {
    // the default kernel name under WSL contains the string "Microsoft" (or "microsoft", depending on the release). 
    // Using uname -r or /proc/version)
    const output = execSync('cat /proc/version').toString();
    return output.includes('microsoft') || output.includes('Microsoft');
  } catch (error) {
    console.error(error);
    return false;
  }
}

export async function installUploaderConfig() {
  // setup uploader config directory
  const uploaderConfigDir = path.join(os.homedir(), '/.superide/uploaders');
  if (!existsAndIsValid(uploaderConfigDir)) {
    fs.mkdirSync(uploaderConfigDir, { recursive: true });
  }

  // download uploader config files
  try {
    await downloadFileIfUpdated('https://raw.githubusercontent.com/leviyanx/uploader/main/uploader.json', 'uploader.json', uploaderConfigDir);
    await downloadFileIfUpdated('https://raw.githubusercontent.com/leviyanx/uploader/main/callUploader.py', 'callUploader.py', uploaderConfigDir);
  } catch (error) {
    console.error("Failed to download uploader config files" + error);
  }
}

export async function downloadFileIfUpdated(url, filename, directory) {
  return new Promise((resolve, reject) => {
    const filePath = path.join(directory, filename);
    let lastModified = null;
    if (fs.existsSync(filePath)) {
      const stats = fs.statSync(filePath);
      lastModified = stats.mtime.toUTCString();
    }
    const options = {
      headers: {
        'If-Modified-Since': lastModified,
      },
    };

    const file = fs.createWriteStream(filePath);
    https.get(url, options, function(response) {
      if (response.statusCode === 304) {
        console.log(filename + ' has not been modified since last download');
        file.close();
        resolve();
        return;
      }
      response.pipe(file);
      file.on('finish', function() {
        file.close();
        console.log(filename + ' has been downloaded into ' + directory);
        resolve();
      });
    }).on('error', function(error) {
      console.error(error);
      file.close();
      reject(error);
    });
  });
}

// TODO: remove this function
export function existsAndIsValid(path) {
  try {
    fs.accessSync(path, fs.constants.F_OK);
    return true;
  } catch (err) {
    return false;
  }
}

export function hasWSL(distributionName) {
  return new Promise((resolve, reject) => {
    exec('wsl --list --verbose', (error, stdout, stderr) => {
      if (error) {
        resolve(false);
      } else if (stderr) {
        resolve(false);
      } else {
        const lines = stdout.replace(/\u0000/g, '').toLowerCase().split('\n');
        for (let i=0; i<lines.length; i++) {
          const line = lines[i].trim();

          if (line.includes(distributionName.toLowerCase())) {
            resolve(true);
            return;
          }
        }
        resolve(false);
    }})
  })
}

export function isRemote() {
  if (vscode.env.remoteName) {
    return true;
  } else {
    return false;
  }
}
